/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.geekbang.thinking.in.spring.ioc.overview.dependency.injection;

import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
import org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.Environment;

/**
 * 依赖注入示例
 *
 * @author lixinlei
 */
public class DependencyInjectionDemo {

    public static void main(String[] args) {

//        beanFactory();
//        beanFactory2();
//        beanFactory3();
//        beanFactory4();
        beanFactory5();

    }

    /**
     * 演示两种依赖注入的方式：
     * 1）xml 中硬编码（META-INF/dependency-injection-context.xml）
     * 2）autowire 自动完成依赖注入（META-INF/dependency-injection-context.xml）
     */
    private static void beanFactory() {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
        // 自定义的 Bean
        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");
        System.out.println(userRepository);
    }

    /**
     * 演示依赖注入和依赖查找的不同：
     * 1）依赖的来源不是同一个；
     */
    private static void beanFactory2() {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");

        // 依赖注入，可以找到依赖 beanFactory
        // 内建的依赖
        System.out.println(userRepository.getBeanFactory());

        // 依赖注入的 beanFactory 和用户代码中创建出来的 BeanFactory 不是一个东西
        System.out.println(userRepository.getBeanFactory() == beanFactory); // false

        // 依赖查找，找不到依赖 beanFactory
        System.out.println(beanFactory.getBean(BeanFactory.class)); // NoSuchBeanDefinitionException
    }

    /**
     * 演示依赖注入用户代码创建的 Context
     */
    private static void beanFactory3() {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");

        ObjectFactory<User> userObjectFactory = userRepository.getUserObjectFactory();
        System.out.println(userObjectFactory.getObject());

        ObjectFactory<ApplicationContext> objectFactory = userRepository.getObjectFactory();
        System.out.println(objectFactory.getObject() == beanFactory); // true
        System.out.println(beanFactory);               // org.springframework.context.support.ClassPathXmlApplicationContext@6267c3bb
        System.out.println(objectFactory.getObject()); // org.springframework.context.support.ClassPathXmlApplicationContext@6267c3bb
    }

    /**
     * 演示容器内建 Bean 对象
     */
    private static void beanFactory4() {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");

        Environment environment = beanFactory.getBean(Environment.class);
        System.out.println(environment); // StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}
    }

    /**
     * Spring IoC 容器到底是 BeanFactory 还是 ApplicationContext？
     * 1）都是；
     */
    private static void beanFactory5() {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
        UserRepository userRepository = (UserRepository) beanFactory.getBean("userRepository");

        // 这里依赖注入的 beanFactory 是 AbstractRefreshableApplicationContext 组合来的 DefaultListableBeanFactory
        System.out.println(userRepository.getBeanFactory()); // org.springframework.beans.factory.support.DefaultListableBeanFactory@90f6bfd

        /**
         * 为什么不一样？
         * userRepository.getBeanFactory() 拿到的 BeanFactory 是在 AbstractRefreshableApplicationContext 中组合的一个
         * BeanFactory 实现 DefaultListableBeanFactory，和用户代码中创建的 beanFactory 不是一个对象；
         */
        System.out.println(userRepository.getBeanFactory() == beanFactory); // false
    }

}
